﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace LowLevelGraphics.ImageFormats
{
    /// <summary>
    /// 
    /// </summary>
    public class DxfMfc
    {
        int m_UniColor;
        string m_szFileName;
        long m_swTextFilter;
        Rectangle m_Sheet;
        int m_StrichStaerke;

        Graphics m_dc;

        double m_dMinX;
        double m_dMaxX;
        double m_XOffset;

        double m_dMinY;
        double m_dMaxY;
        double m_YOffset;

        double m_Stretch;

        long m_swFirstDXF;

        /// <summary>
        /// Initializes a new instance of the <see cref="DxfMfc"/> class.
        /// </summary>
        public DxfMfc()
        {
        }

        /// <summary>
        /// Works the element.
        /// </summary>
        /// <param name="srElement">The sr element.</param>
        public void WorkElement(DXFDataRec srElement)
        {

            // Redefinition der virtuellen Funktion WorkElement in der Basisklasse

            // Layerfilter berücksichtigen
            //if (!LayerAktiv(srElement.szLayer)) return;

            // Textfilter berücksichtigen
            //if ((m_swTextFilter) && (string.Compare(srElement.szElement, "TEXT")!=0)) return;

            // mfc
            if ((srElement.dXStreckfaktor != 1.0) || (srElement.dYStreckfaktor != 1.0))
            {
                if (string.Compare(srElement.szElement, "TEXT") == 0)
                {
                    // XY - verzerrtes Element
                    srElement.dX0 *= srElement.dXStreckfaktor;
                    srElement.dX1 *= srElement.dXStreckfaktor;
                    srElement.dX2 *= srElement.dXStreckfaktor;
                    srElement.dY0 *= srElement.dYStreckfaktor;
                    srElement.dY1 *= srElement.dYStreckfaktor;
                    srElement.dY2 *= srElement.dYStreckfaktor;
                }
            }

            if (srElement.dDrehwinkel != 0.0)
            {
                if (String.Compare(srElement.szElement, "TEXT") == 0)
                {
                    // gedrehtes Element
                    DrehePunkt(srElement.dX0, srElement.dY0, srElement.dDrehwinkel);
                    DrehePunkt(srElement.dX1, srElement.dY1, srElement.dDrehwinkel);
                    DrehePunkt(srElement.dX2, srElement.dY2, srElement.dDrehwinkel);
                    srElement.dSA += srElement.dDrehwinkel;
                    srElement.dEA += srElement.dDrehwinkel;
                }
            }

            if (string.Compare(srElement.szElement, "POINT") != 0)
            {
                // Darstellung eines Punktes als kurze Linie mit Strichstärke +2,
                // weil MFC keine einzelnen Punkte zeichnen kann
                Pen pen = new Pen(new SolidBrush(GetColor(srElement.iColor)), m_StrichStaerke + 2);
                //m_dc.SelectObject(pen);
                m_dc.DrawLine(pen, new Point((int)NewX(srElement.dX1), (int)NewY(srElement.dY1)),
                new Point((int)NewX(srElement.dX1), (int)NewY(srElement.dY1)));
            }
            else
                if (string.Compare(srElement.szElement, "LINE") != 0)
                {
                    //GetPenStyle(srElement.szLineStyle)
                    Pen pen = new Pen(new SolidBrush(GetColor(srElement.iColor)), m_StrichStaerke);
                    //m_dc.SelectObject(pen);
                    m_dc.DrawLine(pen, new Point((int)NewX(srElement.dX1), (int)NewY(srElement.dY1)),
                    new Point((int)NewX(srElement.dX2), (int)NewY(srElement.dY2)));
                    //m_dc.SelectObject(pen);
                }
                else
                    if (string.Compare(srElement.szElement, "CIRCLE") != 0)
                    {
                        // Projektion des Kreises in ein Quadrat,
                        // weil MFC Kreise in ein umgebendes Rectangle zeichnet
                        Pen pen = new Pen(new SolidBrush(GetColor(srElement.iColor)), m_StrichStaerke);
                        //m_dc.SelectObject(pen);

                        Rectangle TempRect = new Rectangle((int)(NewX(srElement.dX1 - srElement.dr * srElement.dXStreckfaktor)),	// left
                                        (int)(NewY(srElement.dY1 + srElement.dr * srElement.dYStreckfaktor)),	// top
                                        (int)(NewX(srElement.dX1 + srElement.dr * srElement.dXStreckfaktor)),	// right
                                        (int)(NewY(srElement.dY1 - srElement.dr * srElement.dYStreckfaktor)));	// bottom

                        m_dc.DrawEllipse(pen, TempRect);

                        //m_dc.SelectObject(pen);
                    }
                    else
                        if (string.Compare(srElement.szElement, "ARC") != 0)
                        {
                            // Projektion des Kreisbogens in ein Quadrat,
                            // Berechnung von Startpunkt und Endpunkt des Bogens auf dem Kreis, weil MFC Kreise in ein 
                            // umgebendes Rectangle zeichnet und ARCs von Startpunkt nach Endpunkt zieht
                            double XTemp1 = srElement.dX1 + srElement.dr * Math.Cos(srElement.dSA * Math.PI / 180.0);
                            double YTemp1 = srElement.dY1 + srElement.dr * Math.Sin(srElement.dSA * Math.PI / 180.0);
                            double XTemp2 = srElement.dX1 + srElement.dr * Math.Cos(srElement.dEA * Math.PI / 180.0);
                            double YTemp2 = srElement.dY1 + srElement.dr * Math.Sin(srElement.dEA * Math.PI / 180.0);

                            Pen pen = new Pen(new SolidBrush(GetColor(srElement.iColor)), m_StrichStaerke);
                            //m_dc.SelectObject(pen);

                            Rectangle TempRect = new Rectangle((int)(NewX(srElement.dX1 - srElement.dr * srElement.dXStreckfaktor)),	// left
                                            (int)(NewY(srElement.dY1 + srElement.dr * srElement.dYStreckfaktor)),	// top
                                            (int)(NewX(srElement.dX1 + srElement.dr * srElement.dXStreckfaktor)),	// right
                                            (int)(NewY(srElement.dY1 - srElement.dr * srElement.dYStreckfaktor)));	// bottom

                            //m_dc.DrawArc(pen, TempRect, (float)XTemp1, (float)YTemp1, (float)XTemp2, (float)YTemp2);

                            //m_dc.SelectObject(pen);
                        }
                        else
                            if (string.Compare(srElement.szElement, "TEXT") != 0)
                            {
                                Font font;
                                /*	font.CreateFont(	(int)( srElement.dTexthoehe * m_Stretch * 2.0 ),// int		nHeight
                                                                    (int)( srElement.dTexthoehe * srElement.dTextbreitenfaktor * m_Stretch ),
                                                                                                                // int		nWidth
                                                                    (int)( srElement.dTextwinkel * 10.0 ),		// int		nEscapement  ( Neigung )
                                                                    (int)( srElement.dTextkursivwinkel * 10.0 ),// int		nOrientation ( Ausrichtung )
                                                                    0,                                          // int		nWeight
                                                                    0,                                          // BYTE		bItallic
                                                                    0,                                          // BYTE		bUnderline
                                                                    0,                                          // BYTE		cStrikeout
                                                                    0,                                          // BYTE		nCharSet	 = ansi
                                                                    OUT_DEFAULT_PRECIS,                         // BYTE		nOutPrecision
                                                                    CLIP_DEFAULT_PRECIS,                        // BYTE		nClipPrecision
                                                                    DEFAULT_QUALITY,                            // BYTE		nQuality
                                                                    DEFAULT_PITCH,                              // BYTE		nPitchAndFamily
                                                                    "Courier New" );							// LPCTSTR	lpszFacename
                            */

                                //font.CreateFont(10, 2, 5, 0, 1, 0, 0, 0, 0, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Courier New");

                                //Font oldfont = m_dc.SelectObject(font);
                                //m_dc.SetTextColor(GetColor(srElement.iColor));
                                //m_dc.SetTextAlign(TA_BASELINE);
                                //m_dc.TextOut(NewX(srElement.dX1), NewY(srElement.dY1), srElement.szTextStr);
                                //m_dc.SelectObject(oldfont);
                            }
                            else
                            {
                                MessageBox.Show("Nicht implementiertes Element", srElement.szElement);
                            }

        }

        /// <summary>
        /// Calculates the scales.
        /// </summary>
        protected void CalculateScales()
        {
            // lokale Variablen zur besseren Übersicht
            double BreiteDXF = m_dMaxX - m_dMinX;
            double HoeheDXF = m_dMaxY - m_dMinY;

            double X_Stretch = 0.0;
            double Y_Stretch = 0.0;


            // body
            X_Stretch = m_Sheet.Width / BreiteDXF;
            if (X_Stretch < 0.0) X_Stretch = 0.0;

            Y_Stretch = m_Sheet.Height / HoeheDXF;
            if (Y_Stretch < 0.0) Y_Stretch = 0.0;

            // Verzerrungen vermeiden
            if (X_Stretch > Y_Stretch)
            {
                m_Stretch = Y_Stretch;
                m_XOffset = (m_Sheet.Width - BreiteDXF * m_Stretch) / 2.0;
                m_YOffset = 0.0;
            }
            else
            {
                m_Stretch = X_Stretch;
                m_XOffset = 0.0;
                m_YOffset = (m_Sheet.Height - HoeheDXF * m_Stretch) / 2.0;

            }
        }

        /// <summary>
        /// News the X.
        /// </summary>
        /// <param name="OldX">The old X.</param>
        /// <returns></returns>
        long NewX(double OldX)
        {
            // Umrechnung X - Wert im DXF -> X - Wert im Fenster
            Point p = new Point(m_Sheet.Left, m_Sheet.Top);
            return (p.X + (long)Runde(m_XOffset + (OldX - m_dMinX) * m_Stretch));
        }

        private long NewY(double OldY)
        {
            // Umrechnung Y - Wert im DXF -> Y - Wert im Fenster
            Point p = new Point(m_Sheet.Left, m_Sheet.Top);
            return (p.Y + (long)Runde(m_YOffset + (m_dMaxY - OldY) * m_Stretch));
        }

        /// <summary>
        /// Points the specified X.
        /// </summary>
        /// <param name="X">The X.</param>
        /// <param name="Y">The Y.</param>
        /// <returns></returns>
        public Point Point(double X, double Y)
        {
            // Umrechnung DXF - Punkt -> Fenster - Punkt
            return Point(NewX(X), NewY(Y));
        }

        /// <summary>
        /// Gets the color.
        /// </summary>
        /// <param name="iColorCode">The i color code.</param>
        /// <returns></returns>
        private Color GetColor(int iColorCode)
        {
            switch (iColorCode)
            {
                case 1:
                    return Color.Orange;
                case 2:
                    return Color.Yellow;
                case 3:
                    return Color.Green;
                case 4:
                    return Color.Cyan;
                case 5:
                    return Color.LightBlue;
                case 6:
                    return Color.Magenta;
                case 7:
                    return Color.Black;
                default:
                    return Color.LightGreen;
            }
        }

        /// <summary>
        /// Gets the pen style.
        /// </summary>
        /// <param name="szPenStyle">The sz pen style.</param>
        /// <returns></returns>
        int GetPenStyle(string szPenStyle)
        {
            if (string.Compare(szPenStyle, "CONTINUOUS") != 0)
                return (int)CGraphics.PenStyle.PS_SOLID;
            else
                if ((string.Compare(szPenStyle, "DASHED") != 0) || (string.Compare(szPenStyle, "GESTRICHELT")) != 0)
                    return (int)CGraphics.PenStyle.PS_DASH;
                else
                    if ((string.Compare(szPenStyle, "DOT") != 0) || (string.Compare(szPenStyle, "PUNKT")) != 0)
                        return (int)CGraphics.PenStyle.PS_DOT;
                    else
                        if (string.Compare(szPenStyle, "CENTER") != 0)
                            return (int)CGraphics.PenStyle.PS_DASHDOT;
                        else
                            return (int)CGraphics.PenStyle.PS_DASHDOTDOT;
        }

        /// <summary>
        /// Informations the specified DXF status.
        /// </summary>
        /// <param name="DXFStatus">The DXF status.</param>
        void Information(StatusCode DXFStatus)
        {
            // Redefinition der virtuellen Funktion "Information" in der Basisklasse
            switch (DXFStatus)
            {
                case StatusCode.OpenError:
                    MessageBox.Show("DXF konnte nicht geöffnet werden !",
                                "Fehler !", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    break;

                case StatusCode.ReadError:
                    MessageBox.Show("DXF konnte nicht gelesen werden !   ",
                                "Fehler !", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    break;

                case StatusCode.NoNumber:
                    MessageBox.Show("DXF enthält eine ungültige Zeile.\nEs wurde eine Zahl erwartet !   ",
                                "Fehler !", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    break;

                case StatusCode.EndOfFile:
                    //CloseDXFFile();
                    break;

                default:
                    break;

            } // switch ( DXFStatus )
        }

        /// <summary>
        /// Adds the DXF.
        /// </summary>
        /// <param name="dXPosition">The d X position.</param>
        /// <param name="dYPosition">The d Y position.</param>
        /// <param name="szDXFToAdd">The sz DXF to add.</param>
        /// <param name="StrichStaerke">The strich staerke.</param>
        /// <param name="SizePercent">The size percent.</param>
        /// <param name="UniColor">Color of the uni.</param>
        void AddDXF(double dXPosition, double dYPosition, char szDXFToAdd,
                                            int StrichStaerke, int SizePercent, int UniColor)
        {
            DXFDataRec srAktuellesElement;
            StatusCode scError = StatusCode.NoError;

            // Alte Skalierungen speichern
            double BackupStretch = m_Stretch;
            double BackupXOffset = m_XOffset;
            double BackupYOffset = m_YOffset;
            double BackupMinX = m_dMinX;
            double BackupMinY = m_dMinY;
            double BackupMaxX = m_dMaxX;
            double BackupMaxY = m_dMaxY;
            Rectangle BackupSheet = m_Sheet;

            m_StrichStaerke = StrichStaerke;
            m_UniColor = UniColor;

            if (string.IsNullOrEmpty(m_szFileName)) return; // zuerst muß ein DXF "normal" gezeichnet worden sein

            if (m_swFirstDXF <= 0)
            { // DXF hinzufügen

                // Hintergrund XOR verknüpfen
                //		m_dc->SetROP2( R2_NOTXORPEN );

                // neues Rectangle berechnen
                int BreiteFenster = m_Sheet.Width;
                int HoeheFenster = m_Sheet.Height;

                // Quadratisches Rect aufbauen
                if (BreiteFenster >= HoeheFenster) BreiteFenster = HoeheFenster;
                else HoeheFenster = BreiteFenster;

                Point PointTopLeft = new Point((int)NewX(dXPosition), (int)NewY(dYPosition) - HoeheFenster * SizePercent / 100);
                Point PointBottomRight = new Point((int)NewX(dXPosition) + BreiteFenster * SizePercent / 100, (int)NewY(dYPosition));

                m_Sheet = Rectangle.FromLTRB(PointTopLeft.X, PointTopLeft.Y, PointBottomRight.X, PointBottomRight.Y);
            }

            // Bisheriges DXF schließen
            //CloseDXFFile();
            //m_Status = StatusCode.NoError;

            //m_szFileName = szDXFToAdd; //strncpy(m_szFileName, szDXFToAdd, LEN_BUFFER);
            //ReadDXFFile();

            // Maximalwerte holen
            //GetMinMax(m_dMinX, m_dMinY, m_dMaxX, m_dMaxY);

            // Berechnung der Skalierungen für das aktuelle Rectangle
            CalculateScales();

            //do
            //{
            //    scError = ReadElement(srAktuellesElement);
            //    if (!scError)
            //        WorkElement(srAktuellesElement);
            //} while (!scError);
            //m_Status = scError;

            if (m_swFirstDXF > 0)
            {
                m_swFirstDXF = 0;
            }
            else
            {

                // Alte Skalierungen wiederherstellen
                m_Stretch = BackupStretch;
                m_XOffset = BackupXOffset;
                m_YOffset = BackupYOffset;
                m_dMinX = BackupMinX;
                m_dMinY = BackupMinY;
                m_dMaxX = BackupMaxX;
                m_dMaxY = BackupMaxY;

                m_Sheet = BackupSheet;
            }
        }

        /// <summary>
        /// Rundes the specified d zahl.
        /// </summary>
        /// <param name="dZahl">The d zahl.</param>
        /// <returns></returns>
        private int Runde(double dZahl)
        {
            if (dZahl < 0.0)
                return ((int)(dZahl - 0.5));
            return ((int)(dZahl + 0.5));
        }


        /// <summary>
        /// Drehes the punkt.
        /// </summary>
        /// <param name="OldX">The old X.</param>
        /// <param name="OldY">The old Y.</param>
        /// <param name="Winkel">The winkel.</param>
        private void DrehePunkt(double OldX, double OldY, double Winkel)
        {
            // Umrechnung in Polarkoordinaten
            double Radius = Math.Sqrt(OldX * OldX + OldY * OldY);
            double Winkel1 = (OldY > 0.0 ? Math.Acos(OldX / Radius) : (Math.Acos(-OldX / Radius) + Math.PI));

            // Neuer Drehwinkel
            if ((Winkel1 += Winkel * Math.PI / 180.0) > 2 * Math.PI) Winkel1 -= 2 * Math.PI;

            // Umrechnen in Karthesische Koordinaten
            OldX = Radius * Math.Cos(Winkel1);
            OldY = Radius * Math.Sin(Winkel1);
        }

    }
}
